/***************************************************************************
 *   Copyright (C) 2009 by Bernhard Neuhofer   *
 *   mail@bneu.at   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
using namespace std;
#include <map>
#include <iostream>
#include <string>
#include <limits>
#include <cstdlib>
#include "AbstractLp.h"
#include "messageHandler.h"
#include "Scheduler.h"
#include "smOutput.h"
#include "gvtStats.h"
#include "submodelManager.h"
#include "../tools/log.h"
#include "factories.h"


void SubmodelManager::run()
{
    /* Gvt Algo: nr of initial message at this submodel is substracted
    * from message difference counter before sim start. */
    if (_gvtStats==NULL) FILE_LOG(logDEBUG)<<"gvt stats error"<<endl;
    if (_assocMh==NULL) FILE_LOG(logDEBUG)<<"mh error"<<endl;
    else FILE_LOG(logDEBUG)<<_assocMh->totalMsgs()<<endl;
    _gvtStats->setMd(true, _assocMh->totalMsgs());
    FILE_LOG(logDEBUG)<<"run#1"<<endl;
    //Update Output collector
    _smOutput->setSent(_assocMh->totalMsgs());
    FILE_LOG(logDEBUG)<<"run#2 " <<_assocMh->totalMsgs()<<endl;
    FILE_LOG(logDEBUG)<<_gvtStats->getGvt()<<" of "<<_endGvt<<" gvt"<<endl;
    while (_gvtStats->getGvt()<_endGvt)
        _assocSched->next()->doStep();

    FILE_LOG(logDEBUG)<<"SubmodelManager#run finished"<<endl;

}

GvtStats* SubmodelManager::cut(bool cutC)
{

    if (cutC)
    {
        _gvtStats->changeColor();
        _gvtStats->resetTmin();
    }

    //Identify minimum LVT of the LPs in this SM
    _gvtStats->setLvtSm(numeric_limits<double>::max());

    map<unsigned int,AbstractLp*>::iterator it;
    for (it=_lpRefs.begin();it!=_lpRefs.end();++it)
    {
        if (it->second->getLvt()<_gvtStats->getLvtSm())
            _gvtStats->setLvtSm(it->second->getLvt());
    }
    return _gvtStats;
}

SmOutput* SubmodelManager::collectOutput()
{
    return _smOutput;
}


void SubmodelManager::shutdown()
{
    exit(0);
}

MessageHandler* SubmodelManager::getAssocMh()
{
    return _assocMh;
}


void SubmodelManager::setGvt(double gvt)
{
    _gvtStats->setGvt(gvt);
}

void SubmodelManager::initSched(string schedClass, double endGvt)
{
    _assocSched= Factories::instance().createSched(schedClass);
    FILE_LOG(logDEBUG)<<"Returning from Scheduler Creation"<<endl;
    if (!_assocSched)
    {
        FILE_LOG(logERROR)<<"Scheduler can not be NULL"<<endl;
        exit(0);
    }
    _endGvt=endGvt;
}

void SubmodelManager::initLp(unsigned int id, vector<unsigned int>neighbors, string meClass, string lpClass, string adClass)
{

    AbstractLp* newLp;
    AbstractAdapter* newAd;
    ModelEntity* newMe;

    //The Logical Process created

    newLp=Factories::instance().createLp( lpClass,id);
    if (!newLp)
    {
        FILE_LOG(logERROR)<<"LP can not be NULL"<<endl;
        exit(0);
    }
    //The model entity (ME) is instanciated
    newMe=Factories::instance().createMe(meClass,newLp,neighbors);
    if (!newMe)
    {
        FILE_LOG(logERROR)<<"Me can not be NULL"<<endl;
        exit(0);
    }

    if (lpClass=="SourceLp")
    {
        FILE_LOG(logINFO)<<"no adapter necessary for source LPs"<<endl;
        newAd=NULL;
    }
    else
    {
		FILE_LOG(logDEBUG)<<"AdapterClass:"<<adClass;
        newAd=Factories::instance().createAd(adClass);
        if (!newAd)
        {
            FILE_LOG(logERROR)<<"Adapter Class not found!!"<<endl;
            exit(0);
        }
    }
    FILE_LOG(logDEBUG)<<"phase 1 done"<<endl;
    //Binds the Mh, adapter, Me, Output collector and GvtStats object to the LP
    newLp->setAssocMh(_assocMh);
    newLp->setAssocAd(newAd);
    newLp->setAssocMe(newMe);
    newLp->setSmOutput(_smOutput);
    newLp->setGvtStats(_gvtStats);
    FILE_LOG(logDEBUG)<<"phase 2 done"<<endl;
    //Add the ID/reference pair to the hash of local Lps and forward this information to the scheduler
    _lpRefs[id]=newLp;
    _assocSched->updateScheduler(_lpRefs);
    FILE_LOG(logDEBUG)<<"phase 3 done"<<endl;
    //Registers the LP with the associated Mh as local LP
    _assocMh->addClient(id);
    FILE_LOG(logDEBUG)<<"phase 4 done"<<endl;
    FILE_LOG(logDEBUG)<<"ID: "<<id;
    FILE_LOG(logDEBUG)<<" - Neighbors: [";
    for (int i=0; i<neighbors.size();i++)
        cout <<" "<<neighbors[i];
    FILE_LOG(logDEBUG)<<"]";
    FILE_LOG(logDEBUG)<<" - ME: "<<meClass;
    FILE_LOG(logDEBUG)<<" - adapter: "<<adClass<<endl;


}
